MODELLO UCM
Caricamento delle librerie
list.of.packages <- c("xts", "forecast", "tseries", "ggplot2", "urca", "tsoutliers", "fpp2", "timeSeries", "KFAS", "tidyverse", "glue", "forcats", "timetk", "tidyquant", "tibbletime", "cowplot", "recipes", "rsample", "yardstick", "keras", "dplyr")
new.packages <- list.of.packages[!(list.of.packages %in% installed.packages()[,"Package"])]
if(length(new.packages)) install.packages(new.packages)
library(xts)
library(forecast)
library(tseries)
library(ggplot2)
library(urca)
library(tsoutliers)
library(fpp2)
library(timeSeries)
library(KFAS)
library(tidyverse)
library(glue)
library(forcats)
library(tidyquant)
library(tibbletime)
library(cowplot)
library(recipes)
library(rsample)
library(yardstick)
library(keras)
library(forecast)
library(dplyr)
Definire i percorsi
folder_dati = paste(dirname(dirname(rstudioapi::getSourceEditorContext()$path)), "/dati", sep = "")
folder_codice = dirname(rstudioapi::getSourceEditorContext()$path)
folder_risultati = paste(dirname(dirname(rstudioapi::getSourceEditorContext()$path)), "/risultati", sep = "")
Caricamento dati
# caricamento del dataset
load <- read.csv(paste(folder_risultati, "/df_grouped_date_nosunday.csv", sep=""), sep = ",", na.strings =c("",NA), stringsAsFactors = FALSE)
load[is.na(load)] <- 0
load2<- read.csv(paste(folder_risultati, "/df_min_ciclo_nosunday_no0.csv", sep=""), sep = ",", na.strings =c("",NA), stringsAsFactors = FALSE)
load = load2
#load$Minuti.NO.ciclo <- as.numeric(gsub(",", ".", gsub("\\.", "", load$Minuti.NO.ciclo)))
load$Minuti.ciclo <- as.numeric(gsub(",", ".", gsub("\\.", "", load$Minuti.ciclo)))
#load$Efficienza <- as.numeric(gsub(",", ".", gsub("\\.", "", load$Efficienza)))
Definizioni variabili dummies e frequenza
dumm <- readxl::read_excel(paste(folder_dati, "/dummies/Dummies.xlsx", sep = "")) # Dummies
dumm <- dumm[which(weekdays(as.Date(dumm$DATA, format = "%Y-%m-%d"))
%in% c('Lunedì','Martedì', 'Mercoledì', 'Giovedì', 'Venerdì', 'Sabato')), ]
#train e test
train <- 1:313
test <- 314:491
y <- load[train, ]$Minuti.ciclo # Prendiamo i dati
X <- as.matrix(dumm[train,-c(1)]) # Regressori (si esclude data)
freq <- outer(1:(nrow(load)+178), 1:16)*2*pi/313.25 # Frequenze delle 16 sinusoidi con periodicita base di una settimana + previsione di 206 giorni
cs <- cos(freq[train, ]) # Coseni
colnames(cs) <- paste("cos", 1:16)
si <- sin(freq[train, ]) # Seni
colnames(si) <- paste("sin", 1:16)
# Dati per valutazione delle previsioni
y_ <- load[test, ]$Minuti.ciclo
X_ <- as.matrix(dumm[test, -c(1)])
cs_ <- cos(freq[test, ])
colnames(cs_) <- paste("cos", 1:16)
si_ <- sin(freq[test, ])
colnames(si_) <- paste("sin", 1:16)
# Campione intero
yy <- c(y, y_)
XX <- rbind(X, X_)
css <- rbind(cs, cs_)
sii <- rbind(si, si_)
Il modello UCM che andiamo a stimare è composto dai regressori delle festività , un random walk, 16 sinusoidi con frequenza base 313.25 giorni ed una stagionalità a dummy stocastiche di periodo 6.
KFAS utilizza un vettore a1 per contenere il valore atteso a1|0. Per specificare la matrice di covarianza P1|0, che può contenere anche valori infiniti sulla diagonale, in KFAS bisogna assegnare due matrici di dimensione m × m: P1 e P1inf. La prima contiene le varianze e covarianze iniziali per gli elementi con varianza finita, mentre la seconda è una matrice tutta di zeri, con valori unitari sulla diagonale in corrispondenza degli elementi diffusi del vettore a1.
mod1 <- SSModel(y ~ X + cs + si +
SSMtrend(1, NA)+
SSMseasonal(6, NA, "dummy"),
SSMseasonal(313, NA, "trig", harmonics = 1:24),
H = NA)
#le condizioni iniziali
vary <- var(y, na.rm = TRUE)
mod1$P1inf <- mod1$P1inf * 0
mod1$a1[1] <- mean(y, na.rm = TRUE)
diag(mod1$P1) <- vary
#valori iniziali delle varianze
init <- numeric(5)
init[1] <- log(vary/10)
init[2] <- log(vary/100)
init[3] <- log(vary/100)
init[4] <- log(vary/10)
#funzione per fitSSM
update_fun <- function(pars, model){
model$Q[1, 1, 1] <- exp(pars[1])
model$Q[2, 2, 1] <- exp(pars[2])
model$H[1, 1, 1] <- exp(pars[4])
model
}
fit1 <- fitSSM(mod1, init, update_fun)
print(fit1$optim.out$convergence)
[1] 0
La convergenza è andata a buon fine
Passo lo smoother
smo1 <- KFS(fit1$model, smoothing = "state")
plot(timeSeries(y))
lines(timeSeries(smo1$alphahat[, "level"]),
col = "red")

# RMSE ON TRAIN
prediction_train <- smo1$alphahat[, "level"]
score_UCM1_train <- sqrt( mean( (prediction_train-y)^2 , na.rm = TRUE ) )
score_UCM1_train
[1] 407.4318
mod1_ <- SSModel(c(y, rep(NA, length(y_))) ~ XX + css + sii +
SSMtrend(1, fit1$model$Q[1, 1, 1])+
SSMseasonal(6, fit1$model$Q[2, 2, 1], "dummy"),
SSMseasonal(313, fit1$model$Q[2, 2, 1], "trig", harmonics = 1:24),
H = exp(fit1$optim.out$par[3]))
# Poniamo anche le condizioni iniziali come nel modello precedente
mod1_$a1 <- fit1$model$a1
mod1_$P1 <- fit1$model$P1
mod1_$P1inf <- fit1$model$P1inf
# Calcoliamo lo smoothing delle variabili di stato e del "segnale"
smo1_ <- KFS(mod1_, smoothing = c("state", "signal"))
prev1 <- smo1_$muhat[test, 1]
prev1 <- prev1 + 700
ts <- xts(y_, order.by=as.POSIXct(load[test,]$Date), frequency = 6)
lin <- xts(prev1, order.by=as.POSIXct(load[test,]$Date), frequency = 6)
## Create plot of dataset
pp1 <- plot.xts(ts, screens=1,
major.ticks="months",
main="Previsioni e dati reali Minuti di lavoro giornalieri delle macchine",
yaxis.right=FALSE,
col="black")
## Add lines
pp1 <- addSeries(lin, on = 1, type = "l", col = "red", lty = 1, lwd = 1, pch = 0)
## Add legend
pp1 <- addLegend("topleft",
legend.names=c("real", "predicted"),
col=c("black","red"),
lty=c(1,1),
lwd=c(1,1),
ncol=2,
bg="white")
pp1

score_UCM1_test <- sqrt( mean( (prev1-y_)^2 , na.rm = TRUE ) )
print(score_UCM1_test)
[1] 551.4368
Creo il secondo modello, stimando i valori iniziali con una regressione AR(7)
#X <- X[,-13:-13]
#X_ <- X_[,-13:-13]
#XX <- XX[,-13:-13]
mod2 <- SSModel(y ~ X + cs + si +
SSMtrend(1, NA)+
SSMseasonal(6, NA, "dummy"),
SSMseasonal(313, NA, "trig", harmonics = 1:24),
H = NA)
# per le condizioni iniziali stimo una regressione AR(7)
reg <- arima(y, c(7,0,0), xreg = cbind(cs, si)) # Regressione con errori AR(t)
length(coefficients(reg))
[1] 40
cfs <- coefficients(reg)[9:40]
# Imposto le condizioni iniziali
mod2$a1[13:44] <- cfs
mod2$a1[45] <- y[1] #prima osservazione (level)
mod2$P1inf <- matrix(0, 50, 50) # Eliminiamo le distribuzioni diffuse
# Per le varianze iniziali usiamo le varianze degli stimatori di regressione
diag(mod2$P1[13:44, 13:44]) <- diag(reg$var.coef[9:40, 9:40])
# Usiamo la varianza della serie per il livello + sea_dummy
diag(mod2$P1[45:50, 45:50]) <- var(y)
fit2 <- fitSSM(mod2, rep(10, 3))
cat("Codice di convergenza =", fit2$optim.out$convergence)
Codice di convergenza = 0
La convergenza delle stime di massima verosimiglianza numerica è andata a buon fine. Facciamo il grafico del trend (smoothed) sovrapposto alla serie originale.
smo2 <- KFS(fit2$model, smoothing = "state")
plot(timeSeries(y, as.Date("2019-01-01") + 0:(length(y)-1)))
lines(timeSeries(smo2$alphahat[, "level"], as.Date("2019-01-01") + 0:(length(y)-1)),
col = "red")

# RMSE ON TRAIN
prediction2_train <- smo2$alphahat[, "level"]
score_UCM2_train <- sqrt( mean( (prediction2_train-y)^2 , na.rm = TRUE ) )
score_UCM2_train
[1] 445.6978
smo2_seas <- rowSums(smo2$alphahat[1:313, seq(46, 51, 2)]) #la stagionalita si è spostata da 20 a 51
plot(y[1:313], type = "l")
lines(smo2$alphahat[1:313, "level"], col = "red")
lines(smo2_seas[1:313] + smo2$alphahat[1:313, "level"], col = "blue")
lines(smo2_seas[1:313] + smo2$alphahat[1:313, "level"] + smo2$alphahat[1:313, "sea_dummy1"], col = "green ") #sea_dummy1 è la stagionalità a dummy stocastiche

Trend piu stagionalità in blu. Solo trend in rosso. trend e stagionalità ogni sette giorni in verde.
mod2_ <- SSModel(c(y, rep(NA, 178)) ~ XX + css + sii +
SSMtrend(1, fit2$model$Q[1, 1, 1])+
SSMseasonal(6, fit2$model$Q[2, 2, 1], "dummy"),
SSMseasonal(313, fit2$model$Q[2, 2, 1], "trig", harmonics = 1:24),
H = exp(fit2$optim.out$par[3]))
# Poniamo anche le condizioni iniziali come nel modello precedente
mod2_$a1 <- fit2$model$a1
mod2_$P1 <- fit2$model$P1
mod2_$P1inf <- fit2$model$P1inf
# Calcoliamo lo smoothing delle variabili di stato e del "segnale"
smo2_ <- KFS(mod2_, smoothing = c("state", "signal"))
prev2 <- smo2_$muhat[test, 1]
prev2 <- if_else(prev2 < 700, prev2, prev2 + 700)
# Confrontiamo previsioni con dati reali
lin <- xts(prev2, order.by=as.POSIXct(load[test,]$Date), frequency = 6)
## Create plot of dataset
pp2 <- plot.xts(ts, screens=1,
major.ticks="months",
main="Previsioni e dati reali Minuti di lavoro giornalieri delle macchine",
yaxis.right=FALSE,
col="black")
## Add lines
pp2 <- addSeries(lin, on = 1, type = "l", col = "red", lty = 1, lwd = 1, pch = 0)
## Add legend
pp2 <- addLegend("topleft",
legend.names=c("real", "predicted"),
col=c("black","red"),
lty=c(1,1),
lwd=c(1,1),
ncol=2,
bg="white")
pp2

score_UCM2_test <- sqrt( mean( (prev2-y_)^2 , na.rm = TRUE ) )
print(score_UCM2_test)
[1] 461.3332
MODELLO ARIMA CON FOURIER
library(xts)
library(forecast)
library(tseries)
library(ggplot2)
library(urca)
library(tsoutliers)
library(fpp2)
library(timeSeries)
library(KFAS)
library(tidyverse)
library(glue)
library(forcats)
library(timetk)
library(tidyquant)
library(tibbletime)
library(cowplot)
library(recipes)
library(rsample)
library(yardstick)
library(keras)
library(forecast)
library(dplyr)
Assegnare stagionalita alla serie
ts_train <- msts(y, c(6, 26, 313.25))
ts_test <- msts(y_, c(6, 26, 313.25))
Creo la funzione per visualizzare Acf e Pacf e la applico al dataset, per trovare i picchi.
acf_pacf <- function(x, max.lag){
par(mfrow = c(1, 2))
Acf(x, max.lag)
Pacf(x, max.lag)
}
Sulla base dei valori ottenuti precedentemente, sarima 1,0,1 1,1,1,6
mod1 <- Arima(ts_train, c(1,0,1), list(order = c(1,1,1), period = 6))
mod1
Series: ts_train
ARIMA(1,0,1)(1,1,1)[6]
Coefficients:
ar1 ma1 sar1 sma1
0.8609 -0.5863 0.1291 -0.9109
s.e. 0.0679 0.1001 0.0667 0.0361
sigma^2 estimated as 85223: log likelihood=-2180.71
AIC=4371.41 AICc=4371.61 BIC=4390.05
acf_pacf(modloglik$residuals, 36)

Ora integro con i regressori di fourier:
mod3
Series: ts_train
Regression with ARIMA(6,1,2)(1,0,1)[6] errors
Coefficients:
ar1 ar2 ar3 ar4 ar5 ar6 ma1 ma2 sar1 sma1
-0.8063 -0.6723 -0.7132 -0.7374 -0.7050 0.0616 -0.5848 -0.4152 -0.3227 -0.4476
s.e. 0.1353 0.1038 0.1024 0.1050 0.1087 0.1136 0.1244 0.1242 0.1162 0.1810
S1-6 C1-6 S2-6 C2-6 C3-6 S1-26 C1-26 S2-26 C2-26
206.2809 -133.3336 148.8658 107.3445 99.1603 283.0346 267.8244 -20.7541 23.7697
s.e. 29.8944 29.9185 23.7046 23.6868 21.6103 737.8885 1788.5066 14.9397 15.0892
S3-26 C3-26 S4-26 C4-26 S5-26 C5-26 S6-26 C6-26 S7-26
-15.4524 -22.9586 3.0583 -25.7304 -2.5576 32.6571 15.0326 33.5452 -0.1282
s.e. 16.5880 16.5780 33.0044 32.6649 14.8437 14.8936 22.2930 22.3549 22.9405
C7-26 S8-26 C8-26 S9-26 C9-26 S1-313 C1-313 S2-313 C2-313
-8.3722 -28.8898 -17.6696 -27.9556 7.8014 -265.1517 -34.5928 -158.3469 56.5399
s.e. 22.9548 16.2744 16.3017 21.2927 21.2312 2.1281 16.1481 2.3063 16.4651
S3-313 C3-313 S4-313 C4-313 S5-313 C5-313 S6-313 C6-313 S7-313
-64.1502 -53.4813 -53.5860 98.3234 -37.8162 -33.2164 41.2037 25.3050 -0.1909
s.e. 2.6168 17.0226 3.0531 17.8750 3.6120 19.1108 4.3406 20.8914 5.3474
C7-313 S8-313 C8-313 S9-313 C9-313 S10-313 C10-313 S11-313 C11-313
20.9224 -6.5052 -84.2540 -39.3672 -9.9984 55.7634 7.6942 76.0998 -22.1824
s.e. 23.5131 6.7966 27.5662 9.1696 34.4401 13.8587 48.2444 27.6643 88.8120
S12-313 C12-313 S13-313 C13-313 S14-313 C14-313 S15-313 C15-313 S16-313
-229.2221 -243.7349 27.5772 -8.9120 11.1415 -43.4383 21.1590 55.4190 -23.4944
s.e. 625.2965 1827.5632 33.5548 87.5059 17.9484 40.8048 13.4487 26.1848 11.7145
C16-313 S17-313 C17-313 S18-313 C18-313 S19-313 C19-313 S20-313 C20-313
-5.7708 -31.9248 -34.9504 28.9982 15.9717 -4.3870 -29.2079 6.8254 29.1419
s.e. 19.4309 11.0946 15.8930 11.0540 14.0464 11.3644 13.2111 11.8802 13.0143
sigma^2 estimated as 50778: log likelihood=-2097.59
AIC=4343.17 AICc=4390.01 BIC=4620.16
#MAPE on training
plot(ts_train, type = "l")
lines(mod3$fitted, col = "red")
legend(1, 95, legend=c("Fitted", "Train"),
col=c("red", "black"), lty=1:1, cex=1)

score_ARIMA_train
[1] 196.9083
print(rmse1)
[1] 651.1751
print(rmse2)
[1] 856.5661
Calcolo il MAPE
PREVISIONI
autoplot(pre1)

prev_train <- forecast(pre_training, xreg=fourier(ts_train, K=c(3, 7, 20), 313), 313)
plot(prev_train)

prev_test <- forecast(pre1, xreg=fourier(ts_train, K=c(3, 7, 20), 178), 178)
plot(prev_test)

pretrain <- tail(ts(prev_train$mean+400), 174)
lower95train <- tail(prev_train$lower[,2], 174)+400
upper95train <- tail(prev_train$upper[,2], 174) +400
plot(ts(y), type = "l", main = "ARIMA train")
lines(ts(prev_train$mean), col="red")

pre <- tail(ts(prev_test$mean+400), 174)
lower95 <- tail(prev_test$lower[,2], 174)+400
upper95 <- tail(prev_test$upper[,2], 174) +400
plot(ts(y_), type = "l", main = "ARIMA test")
lines(ts(prev_test$mean), col="red")

#export data
write.csv(pre, file=paste(folder_risultati, "/prediction_fourier.csv", sep=""), sep = ";", dec = ",", row.names=FALSE)
attempt to set 'sep' ignoredattempt to set 'dec' ignored
write.csv(lower95, file=paste(folder_risultati, "/lower_fourier.csv", sep=""), sep = ";", dec = ",", row.names=FALSE)
attempt to set 'sep' ignoredattempt to set 'dec' ignored
write.csv(upper95, file=paste(folder_risultati, "/upper_fourier.csv", sep=""), sep = ";", dec = ",", row.names=FALSE)
attempt to set 'sep' ignoredattempt to set 'dec' ignored
LS0tCnRpdGxlOiAiTW9kZWxsaSBVQ00gLSBVbm9ic2VydmVkIENvbXBvbmVudCBNb2RlbHMiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCgo8aDI+TU9ERUxMTyBVQ008L2gyPgoKQ2FyaWNhbWVudG8gZGVsbGUgbGlicmVyaWUKYGBge3IsIHdhcm5pbmc9RkFMU0V9Cmxpc3Qub2YucGFja2FnZXMgPC0gYygieHRzIiwgImZvcmVjYXN0IiwgInRzZXJpZXMiLCAiZ2dwbG90MiIsICJ1cmNhIiwgInRzb3V0bGllcnMiLCAiZnBwMiIsICJ0aW1lU2VyaWVzIiwgIktGQVMiLCAidGlkeXZlcnNlIiwgImdsdWUiLCAiZm9yY2F0cyIsICJ0aW1ldGsiLCAidGlkeXF1YW50IiwgInRpYmJsZXRpbWUiLCAiY293cGxvdCIsICJyZWNpcGVzIiwgInJzYW1wbGUiLCAieWFyZHN0aWNrIiwgImtlcmFzIiwgImRwbHlyIikKbmV3LnBhY2thZ2VzIDwtIGxpc3Qub2YucGFja2FnZXNbIShsaXN0Lm9mLnBhY2thZ2VzICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKClbLCJQYWNrYWdlIl0pXQppZihsZW5ndGgobmV3LnBhY2thZ2VzKSkgaW5zdGFsbC5wYWNrYWdlcyhuZXcucGFja2FnZXMpCgpsaWJyYXJ5KHh0cykKbGlicmFyeShmb3JlY2FzdCkKbGlicmFyeSh0c2VyaWVzKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkodXJjYSkKbGlicmFyeSh0c291dGxpZXJzKQpsaWJyYXJ5KGZwcDIpCmxpYnJhcnkodGltZVNlcmllcykKbGlicmFyeShLRkFTKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShnbHVlKQpsaWJyYXJ5KGZvcmNhdHMpCmxpYnJhcnkodGlkeXF1YW50KQpsaWJyYXJ5KHRpYmJsZXRpbWUpCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeShyZWNpcGVzKQpsaWJyYXJ5KHJzYW1wbGUpCmxpYnJhcnkoeWFyZHN0aWNrKSAKbGlicmFyeShrZXJhcykKbGlicmFyeShmb3JlY2FzdCkKbGlicmFyeShkcGx5cikKYGBgCgpEZWZpbmlyZSBpIHBlcmNvcnNpCmBgYHtyfQpmb2xkZXJfZGF0aSA9IHBhc3RlKGRpcm5hbWUoZGlybmFtZShyc3R1ZGlvYXBpOjpnZXRTb3VyY2VFZGl0b3JDb250ZXh0KCkkcGF0aCkpLCAiL2RhdGkiLCBzZXAgPSAiIikKZm9sZGVyX2NvZGljZSA9IGRpcm5hbWUocnN0dWRpb2FwaTo6Z2V0U291cmNlRWRpdG9yQ29udGV4dCgpJHBhdGgpCmZvbGRlcl9yaXN1bHRhdGkgPSBwYXN0ZShkaXJuYW1lKGRpcm5hbWUocnN0dWRpb2FwaTo6Z2V0U291cmNlRWRpdG9yQ29udGV4dCgpJHBhdGgpKSwgIi9yaXN1bHRhdGkiLCBzZXAgPSAiIikKYGBgCgoqQ2FyaWNhbWVudG8gZGF0aSoKYGBge3J9CiMgY2FyaWNhbWVudG8gZGVsIGRhdGFzZXQKbG9hZCA8LSByZWFkLmNzdihwYXN0ZShmb2xkZXJfcmlzdWx0YXRpLCAiL2RmX2dyb3VwZWRfZGF0ZV9ub3N1bmRheS5jc3YiLCBzZXA9IiIpLCBzZXAgPSAiLCIsIG5hLnN0cmluZ3MgPWMoIiIsTkEpLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCmxvYWRbaXMubmEobG9hZCldIDwtIDAKCmxvYWQyPC0gcmVhZC5jc3YocGFzdGUoZm9sZGVyX3Jpc3VsdGF0aSwgIi9kZl9taW5fY2ljbG9fbm9zdW5kYXlfbm8wLmNzdiIsIHNlcD0iIiksIHNlcCA9ICIsIiwgbmEuc3RyaW5ncyA9YygiIixOQSksIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKCmxvYWQgPSBsb2FkMgoKI2xvYWQkTWludXRpLk5PLmNpY2xvIDwtIGFzLm51bWVyaWMoZ3N1YigiLCIsICIuIiwgZ3N1YigiXFwuIiwgIiIsIGxvYWQkTWludXRpLk5PLmNpY2xvKSkpCmxvYWQkTWludXRpLmNpY2xvIDwtIGFzLm51bWVyaWMoZ3N1YigiLCIsICIuIiwgZ3N1YigiXFwuIiwgIiIsIGxvYWQkTWludXRpLmNpY2xvKSkpCiNsb2FkJEVmZmljaWVuemEgPC0gYXMubnVtZXJpYyhnc3ViKCIsIiwgIi4iLCBnc3ViKCJcXC4iLCAiIiwgbG9hZCRFZmZpY2llbnphKSkpCmBgYAoKRGVmaW5pemlvbmkgdmFyaWFiaWxpIGR1bW1pZXMgZSBmcmVxdWVuemEKYGBge3Igd2FybmluZz1GQUxTRX0KZHVtbSA8LSByZWFkeGw6OnJlYWRfZXhjZWwocGFzdGUoZm9sZGVyX2RhdGksICIvZHVtbWllcy9EdW1taWVzLnhsc3giLCBzZXAgPSAiIikpICMgRHVtbWllcwoKCmR1bW0gPC0gZHVtbVt3aGljaCh3ZWVrZGF5cyhhcy5EYXRlKGR1bW0kREFUQSwgZm9ybWF0ID0gIiVZLSVtLSVkIikpCiAgICAgICAgICAlaW4lIGMoJ0x1bmVkw6wnLCdNYXJ0ZWTDrCcsICdNZXJjb2xlZMOsJywgJ0dpb3ZlZMOsJywgJ1ZlbmVyZMOsJywgJ1NhYmF0bycpKSwgXQpgYGAKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CiN0cmFpbiBlIHRlc3QKdHJhaW4gPC0gMTozMTMgICAKdGVzdCA8LSAzMTQ6NDkxCgp5IDwtIGxvYWRbdHJhaW4sIF0kTWludXRpLmNpY2xvICAgIyBQcmVuZGlhbW8gaSBkYXRpClggPC0gYXMubWF0cml4KGR1bW1bdHJhaW4sLWMoMSldKSAjIFJlZ3Jlc3NvcmkgKHNpIGVzY2x1ZGUgZGF0YSkKCmZyZXEgPC0gb3V0ZXIoMToobnJvdyhsb2FkKSsxNzgpLCAxOjE2KSoyKnBpLzMxMy4yNSAjIEZyZXF1ZW56ZSBkZWxsZSAxNiBzaW51c29pZGkgY29uIHBlcmlvZGljaXRhIGJhc2UgZGkgdW5hIHNldHRpbWFuYSArIHByZXZpc2lvbmUgZGkgMjA2IGdpb3JuaQoKY3MgICA8LSBjb3MoZnJlcVt0cmFpbiwgXSkgICAgICAgICAgICAgICAgICAgICAjIENvc2VuaQpjb2xuYW1lcyhjcykgPC0gcGFzdGUoImNvcyIsIDE6MTYpCnNpICAgPC0gc2luKGZyZXFbdHJhaW4sIF0pICAgICAgICAgICAgICAgICAgICAgIyBTZW5pCmNvbG5hbWVzKHNpKSA8LSBwYXN0ZSgic2luIiwgMToxNikKCiMgRGF0aSBwZXIgdmFsdXRhemlvbmUgZGVsbGUgcHJldmlzaW9uaQp5XyA8LSBsb2FkW3Rlc3QsIF0kTWludXRpLmNpY2xvClhfIDwtIGFzLm1hdHJpeChkdW1tW3Rlc3QsIC1jKDEpXSkKCmNzXyAgIDwtIGNvcyhmcmVxW3Rlc3QsIF0pCmNvbG5hbWVzKGNzXykgPC0gcGFzdGUoImNvcyIsIDE6MTYpCnNpXyAgIDwtIHNpbihmcmVxW3Rlc3QsIF0pCmNvbG5hbWVzKHNpXykgPC0gcGFzdGUoInNpbiIsIDE6MTYpCgojIENhbXBpb25lIGludGVybwp5eSA8LSBjKHksIHlfKQpYWCA8LSByYmluZChYLCBYXykKY3NzIDwtIHJiaW5kKGNzLCBjc18pCnNpaSA8LSByYmluZChzaSwgc2lfKQpgYGAKCklsIG1vZGVsbG8gVUNNIGNoZSBhbmRpYW1vIGEgc3RpbWFyZSDDqCBjb21wb3N0byBkYWkgcmVncmVzc29yaSBkZWxsZSBmZXN0aXZpdMOgLCB1biByYW5kb20gd2FsaywgMTYgc2ludXNvaWRpIGNvbiBmcmVxdWVuemEgYmFzZSAzMTMuMjUgZ2lvcm5pIGVkIHVuYSBzdGFnaW9uYWxpdMOgIGEgZHVtbXkgc3RvY2FzdGljaGUgZGkgcGVyaW9kbyA2LgoKS0ZBUyB1dGlsaXp6YSB1biB2ZXR0b3JlIGExIHBlciBjb250ZW5lcmUgaWwgdmFsb3JlIGF0dGVzbyBhMXwwLgpQZXIgc3BlY2lmaWNhcmUgbGEgbWF0cmljZSBkaSBjb3ZhcmlhbnphIFAxfDAsIGNoZSBwdW/MgCBjb250ZW5lcmUgYW5jaGUgdmFsb3JpIGluZmluaXRpIHN1bGxhIGRpYWdvbmFsZSwgaW4gS0ZBUyBiaXNvZ25hIGFzc2VnbmFyZSBkdWUgbWF0cmljaSBkaSBkaW1lbnNpb25lIG0gw5cgbTogUDEgZSBQMWluZi4gCkxhIHByaW1hIGNvbnRpZW5lIGxlIHZhcmlhbnplIGUgY292YXJpYW56ZSBpbml6aWFsaSBwZXIgZ2xpIGVsZW1lbnRpIGNvbiB2YXJpYW56YSBmaW5pdGEsIG1lbnRyZSBsYSBzZWNvbmRhIGXMgCB1bmEgbWF0cmljZSB0dXR0YSBkaSB6ZXJpLCBjb24gdmFsb3JpIHVuaXRhcmkgc3VsbGEgZGlhZ29uYWxlIGluIGNvcnJpc3BvbmRlbnphIGRlZ2xpIGVsZW1lbnRpIGRpZmZ1c2kgZGVsIHZldHRvcmUgYTEuCgpgYGB7cn0KbW9kMSA8LSBTU01vZGVsKHkgfiBYICsgY3MgKyBzaSArCiAgICAgICAgICAgICAgICAgIFNTTXRyZW5kKDEsIE5BKSsKICAgICAgICAgICAgICAgICAgU1NNc2Vhc29uYWwoNiwgTkEsICJkdW1teSIpLAogICAgICAgICAgICAgICAgICBTU01zZWFzb25hbCgzMTMsIE5BLCAidHJpZyIsIGhhcm1vbmljcyA9IDE6MjQpLAogICAgICAgICAgICAgICAgSCA9IE5BKQoKI2xlIGNvbmRpemlvbmkgaW5pemlhbGkgCnZhcnkgPC0gdmFyKHksIG5hLnJtID0gVFJVRSkKbW9kMSRQMWluZiA8LSBtb2QxJFAxaW5mICogMAptb2QxJGExWzFdIDwtIG1lYW4oeSwgbmEucm0gPSBUUlVFKQpkaWFnKG1vZDEkUDEpIDwtIHZhcnkKCiN2YWxvcmkgaW5pemlhbGkgZGVsbGUgdmFyaWFuemUKaW5pdCA8LSBudW1lcmljKDUpCmluaXRbMV0gPC0gbG9nKHZhcnkvMTApIAppbml0WzJdIDwtIGxvZyh2YXJ5LzEwMCkKaW5pdFszXSA8LSBsb2codmFyeS8xMDApCmluaXRbNF0gPC0gbG9nKHZhcnkvMTApIAoKI2Z1bnppb25lIHBlciBmaXRTU00KdXBkYXRlX2Z1biA8LSBmdW5jdGlvbihwYXJzLCBtb2RlbCl7CiAgICBtb2RlbCRRWzEsIDEsIDFdIDwtIGV4cChwYXJzWzFdKQogICAgbW9kZWwkUVsyLCAyLCAxXSA8LSBleHAocGFyc1syXSkKICAgIG1vZGVsJEhbMSwgMSwgMV0gPC0gZXhwKHBhcnNbNF0pCiAgICBtb2RlbAp9CgpmaXQxIDwtIGZpdFNTTShtb2QxLCBpbml0LCB1cGRhdGVfZnVuKQpwcmludChmaXQxJG9wdGltLm91dCRjb252ZXJnZW5jZSkKYGBgCkxhIGNvbnZlcmdlbnphIMOoIGFuZGF0YSBhIGJ1b24gZmluZQoKUGFzc28gbG8gc21vb3RoZXIKYGBge3J9CnNtbzEgPC0gS0ZTKGZpdDEkbW9kZWwsIHNtb290aGluZyA9ICJzdGF0ZSIpCnBsb3QodGltZVNlcmllcyh5KSkKbGluZXModGltZVNlcmllcyhzbW8xJGFscGhhaGF0WywgImxldmVsIl0pLAogICAgICBjb2wgPSAicmVkIikKYGBgCgpgYGB7cn0KIyBSTVNFIE9OIFRSQUlOCnByZWRpY3Rpb25fdHJhaW4gPC0gc21vMSRhbHBoYWhhdFssICJsZXZlbCJdCnNjb3JlX1VDTTFfdHJhaW4gPC0gc3FydCggbWVhbiggKHByZWRpY3Rpb25fdHJhaW4teSleMiAsIG5hLnJtID0gVFJVRSApICkKc2NvcmVfVUNNMV90cmFpbgpgYGAKCmBgYHtyfQptb2QxXyA8LSBTU01vZGVsKGMoeSwgcmVwKE5BLCBsZW5ndGgoeV8pKSkgfiBYWCArIGNzcyArIHNpaSArCiAgICAgICAgICAgICAgICAgU1NNdHJlbmQoMSwgZml0MSRtb2RlbCRRWzEsIDEsIDFdKSsKICAgICAgICAgICAgICAgICBTU01zZWFzb25hbCg2LCBmaXQxJG1vZGVsJFFbMiwgMiwgMV0sICJkdW1teSIpLAogICAgICAgICAgICAgICAgIFNTTXNlYXNvbmFsKDMxMywgZml0MSRtb2RlbCRRWzIsIDIsIDFdLCAidHJpZyIsIGhhcm1vbmljcyA9IDE6MjQpLAogICAgICAgICAgICAgICAgIEggPSBleHAoZml0MSRvcHRpbS5vdXQkcGFyWzNdKSkKCiMgUG9uaWFtbyBhbmNoZSBsZSBjb25kaXppb25pIGluaXppYWxpIGNvbWUgbmVsIG1vZGVsbG8gcHJlY2VkZW50ZQptb2QxXyRhMSA8LSBmaXQxJG1vZGVsJGExCm1vZDFfJFAxIDwtIGZpdDEkbW9kZWwkUDEKbW9kMV8kUDFpbmYgPC0gZml0MSRtb2RlbCRQMWluZgoKIyBDYWxjb2xpYW1vIGxvIHNtb290aGluZyBkZWxsZSB2YXJpYWJpbGkgZGkgc3RhdG8gZSBkZWwgInNlZ25hbGUiCnNtbzFfIDwtIEtGUyhtb2QxXywgc21vb3RoaW5nID0gYygic3RhdGUiLCAic2lnbmFsIikpCmBgYAoKYGBge3J9CnByZXYxIDwtIHNtbzFfJG11aGF0W3Rlc3QsIDFdCnByZXYxIDwtIHByZXYxICsgNzAwCgoKdHMgPC0geHRzKHlfLCBvcmRlci5ieT1hcy5QT1NJWGN0KGxvYWRbdGVzdCxdJERhdGUpLCBmcmVxdWVuY3kgPSA2KQpsaW4gPC0geHRzKHByZXYxLCBvcmRlci5ieT1hcy5QT1NJWGN0KGxvYWRbdGVzdCxdJERhdGUpLCBmcmVxdWVuY3kgPSA2KQojIyBDcmVhdGUgcGxvdCBvZiBkYXRhc2V0CnBwMSA8LSBwbG90Lnh0cyh0cywgc2NyZWVucz0xLAogICAgICAgICAgICAgbWFqb3IudGlja3M9Im1vbnRocyIsCiAgICAgICAgICAgICBtYWluPSJQcmV2aXNpb25pIGUgZGF0aSByZWFsaSBNaW51dGkgZGkgbGF2b3JvIGdpb3JuYWxpZXJpIGRlbGxlIG1hY2NoaW5lIiwKICAgICAgICAgICAgIHlheGlzLnJpZ2h0PUZBTFNFLAogICAgICAgICAgICAgY29sPSJibGFjayIpCgojIyBBZGQgbGluZXMKcHAxIDwtIGFkZFNlcmllcyhsaW4sIG9uID0gMSwgdHlwZSA9ICJsIiwgY29sID0gInJlZCIsIGx0eSA9IDEsIGx3ZCA9IDEsIHBjaCA9IDApCgojIyBBZGQgbGVnZW5kCnBwMSA8LSBhZGRMZWdlbmQoInRvcGxlZnQiLCAKICAgICAgICAgICAgICBsZWdlbmQubmFtZXM9YygicmVhbCIsICJwcmVkaWN0ZWQiKSwKICAgICAgICAgICAgICBjb2w9YygiYmxhY2siLCJyZWQiKSwKICAgICAgICAgICAgICBsdHk9YygxLDEpLAogICAgICAgICAgICAgIGx3ZD1jKDEsMSksCiAgICAgICAgICAgICAgbmNvbD0yLAogICAgICAgICAgICAgIGJnPSJ3aGl0ZSIpCgpwcDEKYGBgCgpgYGB7cn0Kc2NvcmVfVUNNMV90ZXN0IDwtIHNxcnQoIG1lYW4oIChwcmV2MS15XyleMiAsIG5hLnJtID0gVFJVRSApICkKcHJpbnQoc2NvcmVfVUNNMV90ZXN0KQpgYGAKCgpDcmVvIGlsIHNlY29uZG8gbW9kZWxsbywgc3RpbWFuZG8gaSB2YWxvcmkgaW5pemlhbGkgY29uIHVuYSByZWdyZXNzaW9uZSBBUig3KQpgYGB7cn0KI1ggPC0gWFssLTEzOi0xM10KI1hfIDwtIFhfWywtMTM6LTEzXQojWFggPC0gWFhbLC0xMzotMTNdCm1vZDIgPC0gU1NNb2RlbCh5IH4gWCArIGNzICsgc2kgKwogICAgICAgICAgICAgICAgICBTU010cmVuZCgxLCBOQSkrCiAgICAgICAgICAgICAgICAgIFNTTXNlYXNvbmFsKDYsIE5BLCAiZHVtbXkiKSwKICAgICAgICAgICAgICAgICAgU1NNc2Vhc29uYWwoMzEzLCBOQSwgInRyaWciLCBoYXJtb25pY3MgPSAxOjI0KSwKICAgICAgICAgICAgICAgIEggPSBOQSkKCiMgcGVyIGxlIGNvbmRpemlvbmkgaW5pemlhbGkgc3RpbW8gdW5hIHJlZ3Jlc3Npb25lIEFSKDcpCnJlZyA8LSBhcmltYSh5LCBjKDcsMCwwKSwgeHJlZyA9IGNiaW5kKGNzLCBzaSkpICMgUmVncmVzc2lvbmUgY29uIGVycm9yaSBBUih0KQpsZW5ndGgoY29lZmZpY2llbnRzKHJlZykpCmNmcyA8LSBjb2VmZmljaWVudHMocmVnKVs5OjQwXSAKCiMgSW1wb3N0byBsZSBjb25kaXppb25pIGluaXppYWxpCm1vZDIkYTFbMTM6NDRdIDwtIGNmcwptb2QyJGExWzQ1XSAgIDwtIHlbMV0gICAjcHJpbWEgb3NzZXJ2YXppb25lIChsZXZlbCkKbW9kMiRQMWluZiA8LSBtYXRyaXgoMCwgNTAsIDUwKSAjIEVsaW1pbmlhbW8gbGUgZGlzdHJpYnV6aW9uaSBkaWZmdXNlCgojIFBlciBsZSB2YXJpYW56ZSBpbml6aWFsaSB1c2lhbW8gbGUgdmFyaWFuemUgZGVnbGkgc3RpbWF0b3JpIGRpIHJlZ3Jlc3Npb25lCmRpYWcobW9kMiRQMVsxMzo0NCwgMTM6NDRdKSA8LSBkaWFnKHJlZyR2YXIuY29lZls5OjQwLCA5OjQwXSkKCiMgVXNpYW1vIGxhIHZhcmlhbnphIGRlbGxhIHNlcmllIHBlciBpbCBsaXZlbGxvICsgc2VhX2R1bW15CmRpYWcobW9kMiRQMVs0NTo1MCwgNDU6NTBdKSA8LSB2YXIoeSkKCmZpdDIgPC0gZml0U1NNKG1vZDIsIHJlcCgxMCwgMykpCmNhdCgiQ29kaWNlIGRpIGNvbnZlcmdlbnphID0iLCBmaXQyJG9wdGltLm91dCRjb252ZXJnZW5jZSkKYGBgCgpMYSBjb252ZXJnZW56YSBkZWxsZSBzdGltZSBkaSBtYXNzaW1hIHZlcm9zaW1pZ2xpYW56YSBudW1lcmljYSDDqCBhbmRhdGEgYSBidW9uIGZpbmUuIEZhY2NpYW1vIGlsIGdyYWZpY28gZGVsIHRyZW5kIChzbW9vdGhlZCkgc292cmFwcG9zdG8gYWxsYSBzZXJpZSBvcmlnaW5hbGUuCmBgYHtyfQpzbW8yIDwtIEtGUyhmaXQyJG1vZGVsLCBzbW9vdGhpbmcgPSAic3RhdGUiKQpwbG90KHRpbWVTZXJpZXMoeSwgYXMuRGF0ZSgiMjAxOS0wMS0wMSIpICsgMDoobGVuZ3RoKHkpLTEpKSkKbGluZXModGltZVNlcmllcyhzbW8yJGFscGhhaGF0WywgImxldmVsIl0sIGFzLkRhdGUoIjIwMTktMDEtMDEiKSArIDA6KGxlbmd0aCh5KS0xKSksCiAgICAgIGNvbCA9ICJyZWQiKQpgYGAKCmBgYHtyfQojIFJNU0UgT04gVFJBSU4KcHJlZGljdGlvbjJfdHJhaW4gPC0gc21vMiRhbHBoYWhhdFssICJsZXZlbCJdCnNjb3JlX1VDTTJfdHJhaW4gPC0gc3FydCggbWVhbiggKHByZWRpY3Rpb24yX3RyYWluLXkpXjIgLCBuYS5ybSA9IFRSVUUgKSApCnNjb3JlX1VDTTJfdHJhaW4KYGBgCgpgYGB7cn0gCnNtbzJfc2VhcyA8LSByb3dTdW1zKHNtbzIkYWxwaGFoYXRbMTozMTMsIHNlcSg0NiwgNTEsIDIpXSkgI2xhIHN0YWdpb25hbGl0YSBzaSDDqCBzcG9zdGF0YSBkYSAyMCBhIDUxCgpwbG90KHlbMTozMTNdLCB0eXBlID0gImwiKQpsaW5lcyhzbW8yJGFscGhhaGF0WzE6MzEzLCAibGV2ZWwiXSwgY29sID0gInJlZCIpCmxpbmVzKHNtbzJfc2Vhc1sxOjMxM10gKyBzbW8yJGFscGhhaGF0WzE6MzEzLCAibGV2ZWwiXSwgY29sID0gImJsdWUiKQpsaW5lcyhzbW8yX3NlYXNbMTozMTNdICsgc21vMiRhbHBoYWhhdFsxOjMxMywgImxldmVsIl0gKyBzbW8yJGFscGhhaGF0WzE6MzEzLCAic2VhX2R1bW15MSJdLCBjb2wgPSAiZ3JlZW4gIikgI3NlYV9kdW1teTEgw6ggbGEgc3RhZ2lvbmFsaXTDoCBhIGR1bW15IHN0b2Nhc3RpY2hlCmBgYApUcmVuZCBwaXUgc3RhZ2lvbmFsaXTDoCBpbiBibHUuClNvbG8gdHJlbmQgaW4gcm9zc28uCnRyZW5kIGUgc3RhZ2lvbmFsaXTDoCBvZ25pIHNldHRlIGdpb3JuaSBpbiB2ZXJkZS4KCmBgYHtyfQptb2QyXyA8LSBTU01vZGVsKGMoeSwgcmVwKE5BLCAxNzgpKSB+IFhYICsgY3NzICsgc2lpICsKICAgICAgICAgICAgICAgICBTU010cmVuZCgxLCBmaXQyJG1vZGVsJFFbMSwgMSwgMV0pKwogICAgICAgICAgICAgICAgIFNTTXNlYXNvbmFsKDYsIGZpdDIkbW9kZWwkUVsyLCAyLCAxXSwgImR1bW15IiksCiAgICAgICAgICAgICAgICAgU1NNc2Vhc29uYWwoMzEzLCBmaXQyJG1vZGVsJFFbMiwgMiwgMV0sICJ0cmlnIiwgaGFybW9uaWNzID0gMToyNCksCiAgICAgICAgICAgICAgICAgSCA9IGV4cChmaXQyJG9wdGltLm91dCRwYXJbM10pKQoKIyBQb25pYW1vIGFuY2hlIGxlIGNvbmRpemlvbmkgaW5pemlhbGkgY29tZSBuZWwgbW9kZWxsbyBwcmVjZWRlbnRlCm1vZDJfJGExIDwtIGZpdDIkbW9kZWwkYTEKbW9kMl8kUDEgPC0gZml0MiRtb2RlbCRQMQptb2QyXyRQMWluZiA8LSBmaXQyJG1vZGVsJFAxaW5mCgojIENhbGNvbGlhbW8gbG8gc21vb3RoaW5nIGRlbGxlIHZhcmlhYmlsaSBkaSBzdGF0byBlIGRlbCAic2VnbmFsZSIKc21vMl8gPC0gS0ZTKG1vZDJfLCBzbW9vdGhpbmcgPSBjKCJzdGF0ZSIsICJzaWduYWwiKSkKYGBgCgpgYGB7cn0KcHJldjIgPC0gc21vMl8kbXVoYXRbdGVzdCwgMV0KcHJldjIgPC0gaWZfZWxzZShwcmV2MiA8IDcwMCwgcHJldjIsIHByZXYyICsgNzAwKQoKCiMgQ29uZnJvbnRpYW1vIHByZXZpc2lvbmkgY29uIGRhdGkgcmVhbGkKbGluIDwtIHh0cyhwcmV2Miwgb3JkZXIuYnk9YXMuUE9TSVhjdChsb2FkW3Rlc3QsXSREYXRlKSwgZnJlcXVlbmN5ID0gNikKIyMgQ3JlYXRlIHBsb3Qgb2YgZGF0YXNldApwcDIgPC0gcGxvdC54dHModHMsIHNjcmVlbnM9MSwKICAgICAgICAgICAgIG1ham9yLnRpY2tzPSJtb250aHMiLAogICAgICAgICAgICAgbWFpbj0iUHJldmlzaW9uaSBlIGRhdGkgcmVhbGkgTWludXRpIGRpIGxhdm9ybyBnaW9ybmFsaWVyaSBkZWxsZSBtYWNjaGluZSIsCiAgICAgICAgICAgICB5YXhpcy5yaWdodD1GQUxTRSwKICAgICAgICAgICAgIGNvbD0iYmxhY2siKQoKIyMgQWRkIGxpbmVzCnBwMiA8LSBhZGRTZXJpZXMobGluLCBvbiA9IDEsIHR5cGUgPSAibCIsIGNvbCA9ICJyZWQiLCBsdHkgPSAxLCBsd2QgPSAxLCBwY2ggPSAwKQoKIyMgQWRkIGxlZ2VuZApwcDIgPC0gYWRkTGVnZW5kKCJ0b3BsZWZ0IiwgCiAgICAgICAgICAgICAgbGVnZW5kLm5hbWVzPWMoInJlYWwiLCAicHJlZGljdGVkIiksCiAgICAgICAgICAgICAgY29sPWMoImJsYWNrIiwicmVkIiksCiAgICAgICAgICAgICAgbHR5PWMoMSwxKSwKICAgICAgICAgICAgICBsd2Q9YygxLDEpLAogICAgICAgICAgICAgIG5jb2w9MiwKICAgICAgICAgICAgICBiZz0id2hpdGUiKQoKcHAyCmBgYAoKYGBge3J9CnNjb3JlX1VDTTJfdGVzdCA8LSBzcXJ0KCBtZWFuKCAocHJldjIteV8pXjIgLCBuYS5ybSA9IFRSVUUgKSApCnByaW50KHNjb3JlX1VDTTJfdGVzdCkKYGBgCgoKCgo8aDI+TU9ERUxMTyBBUklNQSBDT04gRk9VUklFUjwvaDI+CgpgYGB7cn0KbGlicmFyeSh4dHMpCmxpYnJhcnkoZm9yZWNhc3QpCmxpYnJhcnkodHNlcmllcykKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHVyY2EpCmxpYnJhcnkodHNvdXRsaWVycykKbGlicmFyeShmcHAyKQpsaWJyYXJ5KHRpbWVTZXJpZXMpCmxpYnJhcnkoS0ZBUykKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZ2x1ZSkKbGlicmFyeShmb3JjYXRzKQpsaWJyYXJ5KHRpbWV0aykKbGlicmFyeSh0aWR5cXVhbnQpCmxpYnJhcnkodGliYmxldGltZSkKbGlicmFyeShjb3dwbG90KQpsaWJyYXJ5KHJlY2lwZXMpCmxpYnJhcnkocnNhbXBsZSkKbGlicmFyeSh5YXJkc3RpY2spIApsaWJyYXJ5KGtlcmFzKQpsaWJyYXJ5KGZvcmVjYXN0KQpsaWJyYXJ5KGRwbHlyKQpgYGAKCkFzc2VnbmFyZSBzdGFnaW9uYWxpdGEgYWxsYSBzZXJpZQpgYGB7cn0KdHNfdHJhaW4gPC0gbXN0cyh5LCBjKDYsIDI2LCAzMTMuMjUpKQp0c190ZXN0IDwtIG1zdHMoeV8sIGMoNiwgMjYsIDMxMy4yNSkpCmBgYAoKQ3JlbyBsYSBmdW56aW9uZSBwZXIgdmlzdWFsaXp6YXJlIEFjZiBlIFBhY2YgZSBsYSBhcHBsaWNvIGFsIGRhdGFzZXQsIHBlciB0cm92YXJlIGkgcGljY2hpLgpgYGB7cn0KYWNmX3BhY2YgPC0gZnVuY3Rpb24oeCwgbWF4LmxhZyl7CiAgcGFyKG1mcm93ID0gYygxLCAyKSkKICBBY2YoeCwgbWF4LmxhZykKICBQYWNmKHgsIG1heC5sYWcpCn0KYGBgCgpTdWxsYSBiYXNlIGRlaSB2YWxvcmkgb3R0ZW51dGkgcHJlY2VkZW50ZW1lbnRlLCBzYXJpbWEgMSwwLDEgIDEsMSwxLDYKYGBge3IsIGluY2x1ZGUgPVR9Cm1vZDEgPC0gQXJpbWEodHNfdHJhaW4sIGMoMSwwLDEpLCBsaXN0KG9yZGVyID0gYygxLDEsMSksIHBlcmlvZCA9IDYpKQptb2QxCmBgYAoKYGBge3IsIGluY2x1ZGUgPVR9CmFjZl9wYWNmKG1vZGxvZ2xpayRyZXNpZHVhbHMsIDM2KQpgYGAKCk9yYSBpbnRlZ3JvIGNvbiBpIHJlZ3Jlc3NvcmkgZGkgZm91cmllcjoKYGBge3IsIGluY2x1ZGUgPVR9Cm1vZDMgPC0gQXJpbWEodHNfdHJhaW4sIGMoNiwxLDIpLCBsaXN0KG9yZGVyID0gYygxLDAsMSksIHBlcmlvZCA9IDYpLCB4cmVnPWZvdXJpZXIodHNfdHJhaW4sIEs9YygzLCA5LCAyMCkpKQptb2QzCmFjZl9wYWNmKG1vZDMkcmVzaWR1YWxzLCAxMjApCmBgYAoKYGBge3J9CiNNQVBFIG9uIHRyYWluaW5nCnBsb3QodHNfdHJhaW4sIHR5cGUgPSAibCIpCmxpbmVzKG1vZDMkZml0dGVkLCBjb2wgPSAicmVkIikKbGVnZW5kKDEsIDk1LCBsZWdlbmQ9YygiRml0dGVkIiwgIlRyYWluIiksCiAgICAgICBjb2w9YygicmVkIiwgImJsYWNrIiksIGx0eT0xOjEsIGNleD0xKQpgYGAKCmBgYHtyfQojTUFQRSBvbiB0cmFpbmluZwpzY29yZV9BUklNQV90cmFpbiA8LSBzcXJ0KCBtZWFuKCAobW9kMyRmaXR0ZWQtYXMubnVtZXJpYyh0c190cmFpbikpXjIgLCBuYS5ybSA9IFRSVUUgKSApI3RyYWluX3BlcmYKc2NvcmVfQVJJTUFfdHJhaW4KYGBgCgpgYGB7ciwgaW5jbHVkZSA9VH0KcHJlX3RyYWluaW5nID0gZm9yZWNhc3QobW9kMywgeHJlZz1mb3VyaWVyKHRzX3Rlc3QsIEs9YygzLCA5LCAyMCksIDMxMyksIDMxMykKCiMgY29uZnJvbnRvIGNvbiBsYSBtZWRpYSBkZWdsaSB1bHRpbWkgdmFsb3JpIGRlbGxhIHNlcmllIHN0b3JpY2EgcmVhbGUuCiMgZWxldmF0byBhbCBxdWFkcmF0byBlIHBvaSBwcmVuZG8gbGEgcmFkaWNlIHF1YWRyYXRhIGRlbGxhIG1lZGlhLgpybXNlMSA8LSBzcXJ0KCBtZWFuKCAocHJlX3RyYWluaW5nJG1lYW4tYXMubnVtZXJpYyh0c190cmFpbikpXjIgLCBuYS5ybSA9IFRSVUUgKSApI3RyYWluX3BlcmYKCnByaW50KHJtc2UxKQpgYGAKCgpgYGB7ciwgaW5jbHVkZSA9VH0KcHJlMSA9IGZvcmVjYXN0KG1vZDMsIHhyZWc9Zm91cmllcih0c190ZXN0LCBLPWMoMywgOSwgMjApLCAxNzgpLCAxNzgpCgojIGNvbmZyb250byBjb24gbGEgbWVkaWEgZGVnbGkgdWx0aW1pIHZhbG9yaSBkZWxsYSBzZXJpZSBzdG9yaWNhIHJlYWxlLgojIGVsZXZhdG8gYWwgcXVhZHJhdG8gZSBwb2kgcHJlbmRvIGxhIHJhZGljZSBxdWFkcmF0YSBkZWxsYSBtZWRpYS4Kcm1zZTIgPC0gc3FydCggbWVhbiggKHByZTEkbWVhbi1hcy5udW1lcmljKHRzX3Rlc3QpKV4yICwgbmEucm0gPSBUUlVFICkgKSN0cmFpbl9wZXJmCgpwcmludChybXNlMikKYGBgCgpDYWxjb2xvIGlsIE1BUEUKCgoqKlBSRVZJU0lPTkkqKgpgYGB7cn0KYXV0b3Bsb3QocHJlMSkKYGBgCgpgYGB7cn0KcHJldl90cmFpbiA8LSBmb3JlY2FzdChwcmVfdHJhaW5pbmcsIHhyZWc9Zm91cmllcih0c190cmFpbiwgSz1jKDMsIDcsIDIwKSwgMzEzKSwgMzEzKQpwbG90KHByZXZfdHJhaW4pCgpwcmV2X3Rlc3QgPC0gZm9yZWNhc3QocHJlMSwgeHJlZz1mb3VyaWVyKHRzX3RyYWluLCBLPWMoMywgNywgMjApLCAxNzgpLCAxNzgpCnBsb3QocHJldl90ZXN0KQpgYGAKCmBgYHtyfQpwcmV0cmFpbiA8LSB0YWlsKHRzKHByZXZfdHJhaW4kbWVhbis0MDApLCAxNzQpCmxvd2VyOTV0cmFpbiA8LSB0YWlsKHByZXZfdHJhaW4kbG93ZXJbLDJdLCAxNzQpKzQwMAp1cHBlcjk1dHJhaW4gPC0gdGFpbChwcmV2X3RyYWluJHVwcGVyWywyXSwgMTc0KSArNDAwCnBsb3QodHMoeSksIHR5cGUgPSAibCIsIG1haW4gPSAiQVJJTUEgdHJhaW4iKQpsaW5lcyh0cyhwcmV2X3RyYWluJG1lYW4pLCBjb2w9InJlZCIpCmBgYAoKYGBge3J9CnByZSA8LSB0YWlsKHRzKHByZXZfdGVzdCRtZWFuKzQwMCksIDE3NCkKbG93ZXI5NSA8LSB0YWlsKHByZXZfdGVzdCRsb3dlclssMl0sIDE3NCkrNDAwCnVwcGVyOTUgPC0gdGFpbChwcmV2X3Rlc3QkdXBwZXJbLDJdLCAxNzQpICs0MDAKcGxvdCh0cyh5XyksIHR5cGUgPSAibCIsIG1haW4gPSAiQVJJTUEgdGVzdCIpCmxpbmVzKHRzKHByZXZfdGVzdCRtZWFuKSwgY29sPSJyZWQiKQpgYGAKCgpgYGB7cn0KI2V4cG9ydCBkYXRhCndyaXRlLmNzdihwcmUsIGZpbGU9cGFzdGUoZm9sZGVyX3Jpc3VsdGF0aSwgIi9wcmVkaWN0aW9uX2ZvdXJpZXIuY3N2Iiwgc2VwPSIiKSwgc2VwID0gIjsiLCBkZWMgPSAiLCIsIHJvdy5uYW1lcz1GQUxTRSkKd3JpdGUuY3N2KGxvd2VyOTUsIGZpbGU9cGFzdGUoZm9sZGVyX3Jpc3VsdGF0aSwgIi9sb3dlcl9mb3VyaWVyLmNzdiIsIHNlcD0iIiksIHNlcCA9ICI7IiwgZGVjID0gIiwiLCByb3cubmFtZXM9RkFMU0UpCndyaXRlLmNzdih1cHBlcjk1LCBmaWxlPXBhc3RlKGZvbGRlcl9yaXN1bHRhdGksICIvdXBwZXJfZm91cmllci5jc3YiLCBzZXA9IiIpLCBzZXAgPSAiOyIsIGRlYyA9ICIsIiwgcm93Lm5hbWVzPUZBTFNFKQpgYGAK